﻿//  -------------------- iSharePoint -------------------- //
// 
//  Company:	IT Complex, LLC
//  Project:	Smart.Common
//  File name:	SecurityFactory.cs
//  Developer:	Solomatov Igor
//  Created:	15.04.2012
// 
//  -------------------- iSharePoint -------------------- //

using System;
using System.Collections.Generic;

namespace Smart.Common.Security
{
	/// <summary>
	/// Security factory
	/// </summary>
	public class SecurityFactory : ISecurityFactory
	{
		public SPServicesInstance Services { get; set; }

		public SecurityFactory(SPServicesInstance services)
		{
			Services = services;
		}

		/// <summary>
		/// Register security service
		/// </summary>
		public static void Register<TServiceType>(string serviceName)
			where TServiceType : class, ISecurityService, new()
		{
			SPServices.ServiceLocator.Register<ISecurityService, TServiceType>(serviceName);
		}

		/// <summary>
		/// Unregister security service
		/// </summary>
		public static void Unregister(string serviceName)
		{
			SPServices.ServiceLocator.Unregister<ISecurityService>(serviceName);
		}

		/// <summary>
		/// Returns security service
		/// </summary>
		protected TServiceType Get<TServiceType>(string serviceName)
			where TServiceType : class, ISecurityService
		{
			return Services.ServiceLocator.GetInstance<TServiceType>(serviceName);
		}

		/// <summary>
		/// Get service by serviceName
		/// </summary>
		public virtual ISecurityService GetService(string serviceName)
		{
			var service = Get<ISecurityService>(serviceName);

			if (service == null)
				throw new ArgumentOutOfRangeException("serviceName: " + serviceName);

			service.InitContext(Services.SiteId, serviceName);

			service.InitService();

			return service;
		}

		/// <summary>
		/// Create new securable filter
		/// </summary>
		public virtual SecurableFilter CreateFilter(string serviceName)
		{
			var service = GetService(serviceName);

			return service.CreateFilter();
		}

		/// <summary>
		/// Get securable object types
		/// </summary>
		public virtual ICollection<SecurableType> GetSecurableTypes(string serviceName)
		{
			var service = GetService(serviceName);

			return service.GetSecurableTypes();
		}

		/// <summary>
		/// Ensure all securable security assignments
		/// </summary>
		/// <returns>true, if security changed</returns>
		public virtual bool EnsureSecurity(string serviceName, IDictionary<string, object> values)
		{
			var service = GetService(serviceName);

			return service.EnsureSecurity(values);
		}

		/// <summary>
		/// Ensure filtered securable objects of specified type security assignments
		/// </summary>
		/// <returns>true, if security changed</returns>
		public virtual bool EnsureSecurity(string serviceName, SecurableType securableType, IDictionary<string, object> values)
		{
			var service = GetService(serviceName);

			return service.EnsureSecurity(securableType, values);
		}

		/// <summary>
		/// Ensure filtered securable objects of specified type security assignments
		/// </summary>
		/// <returns>true, if security changed</returns>
		public virtual bool EnsureSecurity(string serviceName, SecurableType securableType, SecurableFilter filter, IDictionary<string, object> values)
		{
			var service = GetService(serviceName);

			return service.EnsureSecurity(securableType, filter, values);
		}

		/// <summary>
		/// Ensure specified securable security assignments
		/// </summary>
		/// <returns>true, if security changed</returns>
		public virtual bool EnsureSecurity(string serviceName, string instanceId, IDictionary<string, object> values)
		{
			var service = GetService(serviceName);

			return service.EnsureSecurity(instanceId, values);
		}

		/// <summary>
		/// <para>Ensure specified securable security assignments.</para>
		/// <para>Warning! Should be called from security context and securable should be from security context (aka RunElevated for SharePoint)!</para>
		///  </summary>
		/// <returns>true, if security changed</returns>
		public virtual bool EnsureSecurity(string serviceName, SecurableType securableType, ISecurable securable, IDictionary<string, object> values)
		{
			var service = GetService(serviceName);

			return service.EnsureSecurity(securableType, securable, values);
		}

		/// <summary>
		/// <para>Ensure specified securable security assignments.</para>
		/// <para>Warning! Should be called from security context and securable should be from security context (aka RunElevated for SharePoint)!</para>
		/// </summary>
		/// <returns>true, if security changed</returns>
		public virtual bool EnsureSecurity(string serviceName, SecuritySecurableAssignments securableAssignments, IDictionary<string, object> values)
		{
			var service = GetService(serviceName);

			return service.EnsureSecurity(securableAssignments, values);
		}
	}
}